package com.mutouren.modules.sso.client;

import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mutouren.common.entity.ResultCode;
import com.mutouren.common.entity.ResultInfo;
import com.mutouren.common.entity.SignResultInfo;
import com.mutouren.common.exception.ExceptionUtils;
import com.mutouren.common.log.LogAlias;
import com.mutouren.common.log.LogManager;
import com.mutouren.common.log.Logger;
import com.mutouren.common.session.SessionConfig;
import com.mutouren.common.session.SessionManager;
import com.mutouren.common.session.UserInfo;
import com.mutouren.common.utils.CookieUtils;
import com.mutouren.common.utils.StringUtils;

public class SsoRequestFilter implements Filter{
	
	private static Logger runLogger = LogManager.getLogger(LogAlias.SystemRun.name());
	private static Logger errorLogger = LogManager.getLogger(LogAlias.SystemError.name());
	
	private static final String redirectUrl_suf = "/sso/relogin.action"; 
	
	private String appAlias;
	private String appSecret;
	private Set<String> excludeURI;
	
	private static MappingUser mappingUser;
	
	public static void initMappingUser(MappingUser mapping) {
		mappingUser = mapping;
	}

	@Override
	public void destroy() {
		
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;				
		String actionName = request.getRequestURI().replace(request.getContextPath(),"");
		
		// 例外URI
		if (excludeURI.contains(actionName)) {
			chain.doFilter(request, response);
			return;
		}

		// SSO响应ticket
		if (actionName.indexOf(redirectUrl_suf) >= 0) {
			doSsoTicket(request, response);
			return;
		}
		
		// SSO登录
		UserInfo userInfo = SessionManager.getSession().get(request);
		if (userInfo == null) {
			doSsoLogin(request, response);
		} else {		
			chain.doFilter(request, response);
		}
	}
	
	private void doSsoTicket(HttpServletRequest request, HttpServletResponse response) {
		// 检验参数
		String ticket = request.getParameter("ticket");
		String state = request.getParameter("state");
		if (StringUtils.isBlank(ticket) || state == null) {
			writeResponse(response, "参数ticket、state空值");
			return;
		}
	
		// ticket换取用户
		SignResultInfo<UserInfo> sriUserInfo = SsoProxy.getUserInfo(appAlias, appSecret, ticket);
		if (sriUserInfo.getCode() != ResultCode.SUCCESS.value) {
			writeResponse(response, "ticket换取用户失败：" + sriUserInfo.getMessage());
			return;			
		}
		
		// 用户映射
		UserInfo userInfo;
		try {
			ResultInfo<UserInfo> result = mappingUser.mapping(sriUserInfo.getInfo());
			if (result.getCode() == ResultCode.SUCCESS.value) {
				userInfo = result.getInfo();
			} else {			
				writeResponse(response, "失败映射用户: " + result.getMessage());
				return;
			}
		} catch (Throwable t) {
			writeResponse(response, "用户映射异常：" + t.getMessage());
			return;		
		}
		
		// 写入session
		onLoginSuccessEvent(request, response, userInfo);
		
		// 重定向默认页
		sendRedirect(response, request.getContextPath() + "/");
	}
	
	private void onLoginSuccessEvent(HttpServletRequest request, HttpServletResponse response, UserInfo userInfo) {
		String sessionId = SessionManager.createSessionId();
		userInfo.setLoginTime(new Date().getTime());
				
		// cookie
		CookieUtils.set(response, SessionConfig.cookieName, sessionId, request.getContextPath());
		
		// session
		SessionManager.getSession().set(request, sessionId, userInfo);
	}	
	
	private void doSsoLogin(HttpServletRequest request, HttpServletResponse response) {
		String url = SsoProxy.getSsoLoginUrl(appAlias, getRedirectUrl(request), "hello");
		sendRedirect(response, url);
	}
	
	private void sendRedirect(HttpServletResponse response, String url) {
		clearCache(response);
		
		try {
			response.sendRedirect(url);
		} catch (IOException e) {
			throw ExceptionUtils.doUnChecked(e);
		}		
	}
	
	private void writeResponse(HttpServletResponse response, String message) {
		clearCache(response);
		
		try {
			response.setContentType("text/plain;charset=utf-8");
			response.getWriter().write(message);			
		} catch (Throwable tt) {	
			errorLogger.error("mtr写入网络IO异常", tt);
			throw ExceptionUtils.doUnChecked(tt);
		}	
	}
	
	private void clearCache(HttpServletResponse response) {
		response.setHeader("Pragma","no-cache"); 
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);  
	}	

	@Override
	public void init(FilterConfig config) throws ServletException {
		runLogger.info("SsoRequestFilter init event");
		
		this.appAlias = config.getInitParameter("appAlias") == null ? null : config.getInitParameter("appAlias").trim();
		this.appSecret = config.getInitParameter("appSecret") == null ? null : config.getInitParameter("appSecret").trim();
		String ssoContextUrl = config.getInitParameter("ssoContextUrl") == null ? null : config.getInitParameter("ssoContextUrl").trim();
		
		if (StringUtils.isBlank(this.appAlias) || StringUtils.isBlank(this.appSecret) || StringUtils.isBlank(ssoContextUrl)) {
			throw new RuntimeException("appAlias,appSecret,ssoContextUrl必填项空值");
		}		
		
		this.excludeURI = parseExcludeURI(config.getInitParameter("excludeURI"));			
		SsoProxy.init(ssoContextUrl);
		
		if (mappingUser == null) {
			throw new RuntimeException("用户映射对象mappingUser空值");
		}
		
	}
	
	private Set<String> parseExcludeURI(String value) {
		Set<String> result = new HashSet<String>();
		if (StringUtils.isBlank(value)) {
			return result;
		}
		
		String[] arr = value.split(",");
		for(String s : arr) {
			result.add(s.trim());
		}
		
		return result;
	}
	
	private String getRedirectUrl(HttpServletRequest request) {		
		int index = request.getRequestURL().toString().indexOf("/", 10);
		if (index < 0) {
			return String.format("%s%s", request.getRequestURL().toString(), redirectUrl_suf);
		} else {
			return String.format("%s%s%s", 
					request.getRequestURL().toString().substring(0, index),
					request.getContextPath(),
					redirectUrl_suf);
		}
	}

}
